<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>WebGL-15.绘制简单立方体</title>
</head>
<body>
  <canvas id="glcanvas" width="1280" height="648"></canvas>
  <script type="shader-source" id="vertexShader">
    // 设置浮点精度为中等精度
    precision mediump float;
    // 接收点在canvas坐标系的坐标
    attribute vec3 a_Position;
    // 接收canvas的宽高
    attribute vec2 a_Screen_Size;
    // 接收颜色
    attribute vec4 a_Color;
    // 透传颜色
    varying vec4 v_Color;
    void main() {
      gl_Position = vec4(a_Position, 1.0);

      // 声明要绘制的点的大小
      gl_PointSize = 5.0;
      // 透传颜色
      v_Color = a_Color;
    }
  </script>
  <script type="shader-source" id="fragmentShader">
    // 设置浮点精度为中等精度
    precision mediump float;
    // 接收从顶点着色器透传过来的颜色
    varying vec4 v_Color;
    void main() {
      // 将不同的颜色表示转换成webgl的颜色表示【0，1】
      vec4 color = v_Color / vec4(255, 255, 255, 1);
      // 设置像素颜色为红色
      gl_FragColor = color;
    }
  </script>
  <script id="webgl-helper">
    /**
     * 创建着色器
     * @this gl
     * @param shaderType 着色器类型
     * @param sourceId 着色器源码id
     * @return shader 着色器
    */
    function createShader(shaderType, sourceId) {
      // 获取着色器源码
      var shaderSource = document.querySelector(sourceId).innerHTML;
      // 创建着色器
      var shader = this.createShader(shaderType);
      // 将源码与着色器绑定
      this.shaderSource(shader, shaderSource);
      // 编译着色器
      this.compileShader(shader);
      return shader;
    }

    /**
     * 创建着色器程序&绑定着色器
     * @this gl
     * @param arguments 着色器数组
     * @return program 着色器程序
    */
    function createProgram() {
      // 创建周色器程序
      var program = this.createProgram();
      for (var i = 0; i < arguments.length; i++) {
        // 将着色器挂在着色器程序上
        this.attachShader(program, arguments[i]);
      }
      // 连接着色器程序
      this.linkProgram(program);
      return program;
    }
    /**
    * @param x 圆心坐标
    * @param y 圆心坐标
    * @param outerRadius 外圆半径
    * @param innerRadius 内圆半径
    * @param n 拆分的三角形个数
    * @return { positions, indices } 坐标与索引
    */
    var sin = Math.sin;
    var cos = Math.cos;
    function createRingVertex(x, y, outerRadius, innerRadius, n) {
      var positions = [];
      for (var i = 0; i < n; i++) {
        var angle = i * Math.PI * 2 / n;
        var color = getRandomColor();
        var inner = [x + innerRadius * cos(angle), y + innerRadius * sin(angle)].concat(color);
        var outter = [x + outerRadius * cos(angle), y + outerRadius * sin(angle)].concat(color);
        positions = positions.concat(inner, outter);
      }
      var indices = [];
      for (var j = 0, len = (positions.length / 6); j < len; j += 2) {
        indices.push(
          j, j + 1, (j + 2) % len,
          (j + 2) % len, j + 1, (j + 3) % len,
        );
      }
      return { positions: positions, indices: indices };
    }
    function getRandomColor() {
      const random = () => Math.random() * 255;
      return [random(), random(), random(), 1];
    }
  </script>
  <script src="./helper.js" ></script>
  <script>
    // 生成一个正方体顶点数据
    function createCube(sideLen) {
      var sl = sideLen / 2;
      var points = [
        -sl, -sl, -sl,
        sl, -sl, -sl,
        sl, sl, -sl,
        -sl, sl, -sl,
        -sl, -sl, sl,
        -sl, sl, sl,
        sl, sl, sl,
        sl, -sl, sl,
      ];
      var colors = [
        [255, 0, 0, 1],
        [0, 255, 0, 1],
        [0, 0, 255, 1],
        [255, 255, 0, 1],
        [255, 0, 255, 1],
        [0, 255, 255, 1],
      ];

      var indices = [
        0, 1, 2, // 正面
        0, 2, 3,
        0, 3, 4, // 左边
        4, 3, 5,
        3, 5, 2, // 上面
        2, 5, 6,
        5, 6, 4, // 后面
        4, 6, 7,
        7, 6, 2, // 右边
        7, 2, 1,
        1, 7, 4, // 下面
        1, 4, 0,
      ];
      var _colors = [];
      for (var i = 0; i < indices.length; i++) {
        _colors.push(colors[Math.floor(i / 6)]);
      }
      return {
        colors: _colors,
        indices,
        points,
      };
    }
  </script>
  <script>
    (function() {
      var canvas = document.getElementById('glcanvas');
      var gl = canvas.getContext('webgl');

      // 创建顶点着色器对象
      var vertexShader = createShader.call(gl, gl.VERTEX_SHADER, '#vertexShader');
      // 创建片元着色器对象
      var fragmentShader = createShader.call(gl, gl.FRAGMENT_SHADER, '#fragmentShader');
      // 创建着色器程序
      var program = createProgram.apply(gl, [vertexShader, fragmentShader]);

      // 启用着色器程序（通常有多个程序) 使用前先启动
      gl.useProgram(program);

      // 获取顶点着色器的变量
      var aPosition = gl.getAttribLocation(program, 'a_Position');
      var aScreenSize = gl.getAttribLocation(program, 'a_Screen_Size');
      var aColor = gl.getAttribLocation(program, 'a_Color');
      // 为顶点着色器传递canvas宽高
      gl.vertexAttrib2f(aScreenSize, canvas.width, canvas.height);

      var infos = createCube(0.5);

      // 启用属性
      gl.enableVertexAttribArray(aPosition);
      gl.enableVertexAttribArray(aColor);

      var buffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
      // 将顶点数据写入缓冲区
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(infos.points), gl.STATIC_DRAW);
      gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);


      var colorBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(infos.colors), gl.STATIC_DRAW);
      gl.vertexAttribPointer(aColor, 4, gl.UNSIGNED_BYTE, false, 0, 0);


      var indicesBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(infos.indices), gl.STATIC_DRAW);

      // 绘制三角形
      function draw() {
        // 设置清空画布的颜色为黑色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        // 用上一步设置的颜色清空画布
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES, infos.indices.length, gl.UNSIGNED_SHORT, 0);
      }
      draw();
    })();
  </script>
</body>
</html>